package cn.iocoder.yudao.module.trade.order.service.handler;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.framework.common.exception.KnownServiceException;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.module.api.member.assets.MemberDigitalAssetsApi;
import cn.iocoder.yudao.module.api.member.assets.dto.MemberDigitalAssetsRespDTO;
import cn.iocoder.yudao.module.api.member.assets.enums.CryptocurrencyDeductionType;
import cn.iocoder.yudao.module.api.pay.bill.PayBillApi;
import cn.iocoder.yudao.module.api.pay.bill.dto.PayBillCreateDTO;
import cn.iocoder.yudao.module.api.pay.bill.enums.BillCategoryEnum;
import cn.iocoder.yudao.module.api.pay.bill.enums.BillTypeEnum;
import cn.iocoder.yudao.module.api.shop.info.ShopInfoApi;
import cn.iocoder.yudao.module.api.shop.info.dto.ShopInfoDTO;
import cn.iocoder.yudao.module.trade.order.dal.dataobject.TradeOrderDO;
import cn.iocoder.yudao.module.trade.order.dal.dataobject.TradeOrderItemDO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 用户收益豆的 {@link TradeOrderHandler} 实现类
 *
 * @author 山野羡民
 */
@Slf4j
@Component
public class TradeMemberCryptocurrencyOrderHandler implements TradeOrderHandler {

    @Resource
    private MemberDigitalAssetsApi memberDigitalAssetsApi;
    @Resource
    private ShopInfoApi shopInfoApi;
    @Resource
    private PayBillApi payBillApi;

    @Override
    public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
        Double deductCryptocurrency = order.getDeductCryptocurrency();
        if (deductCryptocurrency == null || deductCryptocurrency <= 0) {
            // 未使用收益豆抵扣
            return;
        }
        MemberDigitalAssetsRespDTO digitalAssets = memberDigitalAssetsApi.getDetail(order.getUserId());
        /*Assertions.isTrue(digitalAssets.getCryptocurrencyIncrement().doubleValue() > deductCryptocurrency || digitalAssets.getCryptocurrencyOrigin().doubleValue() > deductCryptocurrency, "联盟豆或收益豆余额不足");*/
        // 客户明确化：只能用收益豆抵扣，联盟豆必须先主动转换成收益豆，不能系统自动帮转
        if (digitalAssets.getCryptocurrencyOrigin().doubleValue() > deductCryptocurrency) {
            Assertions.isTrue(digitalAssets.getCryptocurrencyIncrement().doubleValue() > deductCryptocurrency, "收益豆余额不足，请先将联盟豆转为收益豆");
        } else {
            throw new KnownServiceException("收益豆余额不足，请先获取联盟豆后再转为收益豆");
        }
    }

    @Override
    public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
        releaseMerchantCryptocurrencyAsync(order);
        Double deductCryptocurrency = order.getDeductCryptocurrency();
        if (deductCryptocurrency == null || deductCryptocurrency == 0) {
            // 未使用收益豆抵扣
            return;
        }
        Long buyerUserId = order.getUserId();
        Long sellerUserId = shopInfoApi.getOwnerMemberUserId(order.getShopId());
        log.debug("[afterPayOrder] 订单【{}】支付，买家用户编号【{}】，卖家店铺编号【{}】，卖家用户编号【{}】", order.getNo(), buyerUserId, order.getShopId(), sellerUserId);
        boolean isExchange = order.getGiveBonus() > 0;
        payCryptocurrencyIncrement(buyerUserId, deductCryptocurrency,
                "订单【%s】支付，使用【%s个】%s".formatted(order.getNo(), deductCryptocurrency, isExchange ? "收益豆兑换" : "收益豆抵现"),
                order.getNo());
        if (isExchange) {
            log.debug("[afterPayOrder] 订单【{}】支付，使用【{}个】收益豆全额兑换产品，赠送【{}个】分红", order.getNo(), deductCryptocurrency, order.getGiveBonus());
            // 用户用收益豆全额兑换，则返买家一定的分红并销毁买家相应的收益豆
            changeBonus(buyerUserId, order.getGiveBonus(), "订单【%s】支付，使用收益豆全额兑换产品赠送【%s个】分红".formatted(order.getNo(), order.getGiveBonus()), order.getNo());
        } else {
            log.debug("[afterPayOrder] 订单【{}】支付，收益豆部分抵扣，买家正向转移给卖家【{}个】收益豆", order.getNo(), deductCryptocurrency);
            // 用户用收益豆部分抵扣，则将买家的收益豆转移到卖家名下
            exchangeCryptocurrencyIncrement(sellerUserId, deductCryptocurrency,
                    BillTypeEnum.PAY_PRODUCT,
                    "订单【%s】支付，获得买家抵扣的收益豆【%s个】".formatted(order.getNo(), deductCryptocurrency),
                    order.getNo());
        }
        Double giveCryptocurrency = order.getGiveCryptocurrency();
        if (giveCryptocurrency != null && giveCryptocurrency > 0) {
            log.debug("[afterPayOrder] 订单【{}】支付，商家赠送【{}个】收益豆", order.getNo(), giveCryptocurrency);
            // 赠送用户收益豆，需校验商家的收益豆是否足够，不够则不赠送
            Double shopCryptocurrency = memberDigitalAssetsApi.getDetail(sellerUserId).getCryptocurrency().doubleValue();
            if (shopCryptocurrency >= giveCryptocurrency) {
                exchangeCryptocurrencyIncrement(buyerUserId, giveCryptocurrency,
                        BillTypeEnum.PAY_PRODUCT,
                        "订单【%s】支付，商家赠送【%s个】收益豆".formatted(order.getNo(), giveCryptocurrency),
                        order.getNo());
                exchangeCryptocurrencyIncrement(sellerUserId, -1 * giveCryptocurrency,
                        BillTypeEnum.PAY_PRODUCT,
                        "订单【%s】支付，赠送用户【%s个】收益豆".formatted(order.getNo(), giveCryptocurrency),
                        order.getNo());
            } else {
                log.warn("[afterPayOrder] 商家收益豆只有{}个，不能赠送用户收益豆{}个", shopCryptocurrency, giveCryptocurrency);
            }
        }
    }

    @Override
    public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
        // 售后的订单项，已经在 afterCancelOrderItem 回滚库存，所以这里不需要重复回滚
        orderItems = filterOrderItemListByNoneAfterSale(orderItems);
        if (CollUtil.isEmpty(orderItems)) {
            return;
        }
        onOrderRefund(order);
    }

    @Override
    public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
        onOrderRefund(order);
    }

    protected void onOrderRefund(TradeOrderDO order) {
        if (!Boolean.TRUE.equals(order.getPayStatus())) {
            // 防御性编程：没有支付过的，说明不是退款的情况下取消订单，不管
            return;
        }
        changeCryptocurrencyWhenOrderRefund(order);
        changeBonusWhenOrderRefund(order);
    }

    private void changeCryptocurrencyWhenOrderRefund(TradeOrderDO order) {
        Double deductCryptocurrency = order.getDeductCryptocurrency();
        if (deductCryptocurrency == null || deductCryptocurrency == 0) {
            // 未使用收益豆抵扣
            return;
        }
        Long buyerUserId = order.getUserId();
        Long sellerUserId = shopInfoApi.getOwnerMemberUserId(order.getShopId());
        log.debug("[onOrderRefund] 订单【{}】支付，买家用户编号【{}】，卖家店铺编号【{}】，卖家用户编号【{}】", order.getNo(), buyerUserId, order.getShopId(), sellerUserId);
        boolean isExchange = order.getGiveBonus() > 0;
        refundCryptocurrencyIncrement(buyerUserId, deductCryptocurrency,
                "订单【%s】关闭，返还%s的【%s个】收益豆".formatted(order.getNo(), isExchange ? "兑换" : "抵现", deductCryptocurrency),
                order.getNo());
        Double giveCryptocurrency = order.getGiveCryptocurrency();
        if (giveCryptocurrency != null && giveCryptocurrency > 0) {
            log.debug("[onOrderRefund] 订单【{}】关闭，扣减/返还商家赠送用户的【{}个】收益豆", order.getNo(), giveCryptocurrency);
            exchangeCryptocurrencyIncrement(buyerUserId, giveCryptocurrency,
                    BillTypeEnum.PAY_PRODUCT_REFUND,
                    "订单【%s】关闭，扣减商家赠送的【%s个】收益豆".formatted(order.getNo(), -1 * giveCryptocurrency),
                    order.getNo());
            exchangeCryptocurrencyIncrement(sellerUserId, giveCryptocurrency,
                    BillTypeEnum.PAY_PRODUCT_REFUND,
                    "订单【%s】关闭，返还赠送用户的【%s个】收益豆".formatted(order.getNo(), giveCryptocurrency),
                    order.getNo());
        }
        if (isExchange) {
            log.debug("[onOrderRefund] 订单【{}】关闭，回收赠送用户的【{}个】分红", order.getNo(), order.getGiveBonus());
            // 用户用收益豆全额兑换，则回收赠送给买家的分红
            changeBonus(buyerUserId, -1d * order.getGiveBonus(), "订单【%s】关闭，回收赠送的【%s个】分红".formatted(order.getNo(), order.getGiveBonus()), order.getNo());
        } else {
            log.debug("[onOrderRefund] 订单【{}】关闭，卖家逆向转移买家的【{}个】收益豆", order.getNo(), deductCryptocurrency);
            // 用户用收益豆部分抵扣，则将转移到卖家名下的收益豆返还给买家
            exchangeCryptocurrencyIncrement(sellerUserId, -1 * deductCryptocurrency, BillTypeEnum.PAY_PRODUCT,
                    "订单【%s】关闭，扣减买家抵扣的收益豆【%s个】".formatted(order.getNo(), deductCryptocurrency),
                    order.getNo());
        }
    }

    private void payCryptocurrencyIncrement(Long userId, Double cryptocurrency, String mark, String tradeNo) {
        CryptocurrencyDeductionType deductionType = memberDigitalAssetsApi.payCryptocurrencyIncrement(userId, cryptocurrency);
        if (deductionType == CryptocurrencyDeductionType.NONE) {
            return;
        }
        PayBillCreateDTO createDTO = new PayBillCreateDTO();
        createDTO.setUserId(userId);
        if (deductionType == CryptocurrencyDeductionType.INCREMENT) {
            createDTO.setCategory(BillCategoryEnum.CRYPTOCURRENCY_INCREMENT);
            createDTO.setMark(mark);
        } else {
            createDTO.setCategory(BillCategoryEnum.CRYPTOCURRENCY_ORIGIN);
            createDTO.setMark(mark.replace("收益豆", "联盟豆"));
        }
        createDTO.setType(BillTypeEnum.PAY_PRODUCT);
        createDTO.setNumber(cryptocurrency);
        createDTO.setRelateId(tradeNo);
        createDTO.setExtendField("");
        payBillApi.expendAsync(createDTO);
    }

    private void refundCryptocurrencyIncrement(Long userId, Double cryptocurrency, String mark, String tradeNo) {
        memberDigitalAssetsApi.refundCryptocurrencyIncrement(userId, cryptocurrency);
        PayBillCreateDTO createDTO = new PayBillCreateDTO();
        createDTO.setUserId(userId);
        createDTO.setCategory(BillCategoryEnum.CRYPTOCURRENCY_INCREMENT);
        createDTO.setType(BillTypeEnum.PAY_PRODUCT_REFUND);
        createDTO.setNumber(cryptocurrency);
        createDTO.setMark(mark);
        createDTO.setRelateId(tradeNo);
        createDTO.setExtendField("");
        payBillApi.incomeAsync(createDTO);
    }

    private void exchangeCryptocurrencyIncrement(Long userId, Double cryptocurrency, BillTypeEnum billType, String mark, String tradeNo) {
        memberDigitalAssetsApi.exchangeCryptocurrencyOriginToIncrement(userId, cryptocurrency);
        PayBillCreateDTO createDTO = new PayBillCreateDTO();
        createDTO.setUserId(userId);
        createDTO.setCategory(BillCategoryEnum.CRYPTOCURRENCY_INCREMENT);
        createDTO.setType(billType);
        createDTO.setNumber(cryptocurrency);
        createDTO.setMark(mark);
        createDTO.setRelateId(tradeNo);
        createDTO.setExtendField("");
        if (cryptocurrency > 0) {
            payBillApi.incomeAsync(createDTO);
        } else {
            payBillApi.expendAsync(createDTO);
        }
    }

    private void changeBonusWhenOrderRefund(TradeOrderDO order) {
        if (order.getGiveBonus() > 0) {
            changeBonus(order.getUserId(), -1d * order.getGiveBonus(), "订单【%s】关闭，返还使用收益豆全额兑换产品后赠送的【%s个】分红".formatted(order.getNo(), order.getGiveBonus()), order.getNo());
        }
    }

    private void changeBonus(Long userId, Double bonus, String mark, String tradeNo) {
        memberDigitalAssetsApi.changeBonus(userId, bonus);
        PayBillCreateDTO createDTO = new PayBillCreateDTO();
        createDTO.setUserId(userId);
        createDTO.setCategory(BillCategoryEnum.BONUS);
        createDTO.setType(BillTypeEnum.EXCHANGE_PRODUCT);
        createDTO.setNumber(bonus);
        createDTO.setMark(mark);
        createDTO.setRelateId(tradeNo);
        createDTO.setExtendField("");
        payBillApi.incomeAsync(createDTO);
    }

    @Async
    public void releaseMerchantCryptocurrencyAsync(TradeOrderDO order) {
        ShopInfoDTO shopInfoRespDTO = shopInfoApi.getShopInfo(order.getShopId());
        Assertions.notNull(shopInfoRespDTO, "未获取到商家信息：shopId=" + order.getShopId());
        double cryptocurrency = MoneyUtils.fenToYuan((int) (order.getPayPrice() * shopInfoRespDTO.getServiceFeePercent() / 100f)).doubleValue();
        MemberDigitalAssetsRespDTO digitalAssetsRespDTO = memberDigitalAssetsApi.getDetail(shopInfoRespDTO.getUserId());
        if (digitalAssetsRespDTO.getCryptocurrencyFreezing().doubleValue() > cryptocurrency) {
            memberDigitalAssetsApi.unfreezeCryptocurrencyOrigin(shopInfoRespDTO.getUserId(), cryptocurrency);
            PayBillCreateDTO createDTO = new PayBillCreateDTO();
            createDTO.setUserId(shopInfoRespDTO.getUserId());
            createDTO.setCategory(BillCategoryEnum.CRYPTOCURRENCY_ORIGIN);
            createDTO.setType(BillTypeEnum.PAY_PRODUCT);
            createDTO.setNumber(cryptocurrency);
            createDTO.setMark("商家【%s】售卖产品入账【%s元】，释放【%s个】联盟豆".formatted(shopInfoRespDTO.getName(), MoneyUtils.fenToYuanStr(order.getPayPrice()), cryptocurrency));
            createDTO.setRelateId(order.getNo());
            createDTO.setExtendField("{\"orderId\":\"%s\"}".formatted(order.getId()));
            payBillApi.incomeAsync(createDTO);
        } else {
            log.warn("商家({})用户({})收款({})，冻结中的联盟豆余额不足，忽略联盟豆释放", shopInfoRespDTO.getName(), shopInfoRespDTO.getUserId(), order.getNo());
        }
    }

}
